﻿#pragma once

#include <xy/geom/camera.h>
#include <xy/render/image.h>

#include <functional>

namespace xy
{

namespace render
{

struct msaa
{
    static constexpr float samples[9][8] = {
        {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
        {1.0f / 2, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
        {1.0f / 3, 2.0f / 3, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
        {1.0f / 4, 2.0f / 4, 3.0f / 4, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
        {1.0f / 5, 2.0f / 5, 3.0f / 5, 4.0f / 5, 0.0f, 0.0f, 0.0f, 0.0f},
        {1.0f / 6, 2.0f / 6, 3.0f / 6, 4.0f / 6, 5.0f / 6, 0.0f, 0.0f, 0.0f},
        {1.0f / 7, 2.0f / 7, 3.0f / 7, 4.0f / 7, 5.0f / 7, 6.0f / 7, 0.0f, 0.0f},
        {1.0f / 8, 2.0f / 8, 3.0f / 8, 4.0f / 8, 5.0f / 8, 6.0f / 8, 7.0f / 8, 0.0f},
        {1.0f / 9, 2.0f / 9, 3.0f / 9, 4.0f / 9, 5.0f / 9, 6.0f / 9, 7.0f / 9, 8.0f / 9},
    };
};

struct painter
{
    image_t frame;
    image_t zBuffer;
    const int msaaLevel;

    /**
     * @brief 创建画框
     *
     * @param[in] width
     * @param[in] height
     * @param[in] msaaLevel
     */
    painter(int width, int height, int msaaLevel);

    /**
     * @brief 重置画框大小
     *
     * @param[in] width
     * @param[in] height
     */
    void resize(int width, int height);

    /**
     * @brief 清空画框
     *
     * @param[in] color
     */
    void clear_color(const color_t &color);

    /**
     * @brief 清空深度缓存
     *
     * @param[in] depth
     */
    void clear_depth(int depth);

    /**
     * @brief Bresenham 画圆法
     *
     * @param[in] x
     * @param[in] y
     * @param[in] r
     * @param[in] color
     */
    void draw_circle(int x, int y, int r, const color_t &color);

    /**
     * @brief 逐点比较法画圆
     *
     * @param[in] x
     * @param[in] y
     * @param[in] r
     * @param[in] color
     */
    void draw_circle_pointwise(int x, int y, int r, const color_t &color);

    /**
     * @brief 简化的 Bresenham 画圆法 https://www.geeksforgeeks.org/bresenhams-circle-drawing-algorithm/
     *
     * @param[in] x
     * @param[in] y
     * @param[in] r
     * @param[in] color
     */
    void draw_circle_bresenham(int x, int y, int r, const color_t &color);

    /**
     * @brief 中点椭圆法
     *
     * @param[in] x
     * @param[in] y
     * @param[in] width
     * @param[in] height
     * @param[in] color
     */
    void draw_ellispe(int x, int y, int width, int height, const color_t &color);

    /**
     * @brief 填充圆形
     *
     * @param[in] x
     * @param[in] y
     * @param[in] r
     * @param[in] color
     */
    void fill_circle(int x, int y, int r, const color_t &color);

    /**
     * @brief 4 连通边界标记填充法
     *
     * @param[in] x
     * @param[in] y
     * @param[in] fillColor
     * @param[in] boundaryColor
     */
    void fill_boundary_4(int x, int y, const color_t &fillColor, const color_t &boundaryColor);

    /**
     * @brief 洪水填充法，填充同色像素
     * https://www.geeksforgeeks.org/flood-fill-algorithm-implement-fill-pastd::size_t/
     *
     * @param[in] x
     * @param[in] y
     * @param[in] fillColor
     * @param[in] sameColor
     */
    void fill_flood_4(int x, int y, const color_t &fillColor, const color_t &sameColor);

    /**
     * @brief 保存画框
     *
     * @param[in] filename
     */
    void write_image(std::string filename);

    /**
     * @brief 绘制单点
     *
     * @param[in] v
     * @param[in] size
     * @param[in] depthFunc
     * @param[in] color
     */
    void draw_point(const float *v, int size, std::function<bool(std::uint32_t, std::uint32_t)> depthFunc,
                    const color_t &color);

    /**
     * @brief Bersenham 法绘制直线
     *
     * @param v0
     * @param v1
     * @param depthFunc
     * @param colorFunc
     */
    void draw_line(const float *v0, const float *v1, std::function<bool(std::uint32_t, std::uint32_t)> depthFunc,
                   std::function<color_t(float)> colorFunc);

    /**
     * @brief 吴晓林反走样画线 https://www.geeksforgeeks.org/anti-aliased-line-xiaolin-wus-algorithm/
     * 使用 Gupta-Sproull 算法思路类似，计算更精确但是开销更大
     *
     * @param v0
     * @param v1
     * @param width
     * @param depthFunc
     * @param colorFunc
     */
    void draw_line_antialias(const float *v0, const float *v1, int width,
                             std::function<bool(std::uint32_t, std::uint32_t)> depthFunc,
                             std::function<color_t(float)> colorFunc);

    /**
     * @brief 填充三角形
     * https://github.com/ssloy/tinyrenderer/wiki/Lesson-2:-Triangle-rasterization-and-back-face-culling
     *
     * @param v0
     * @param v1
     * @param v2
     * @param depthFunc
     * @param colorFunc
     */
    void fill_triangle(const float *v0, const float *v1, const float *v2,
                       std::function<bool(std::uint32_t, std::uint32_t)> depthFunc,
                       std::function<color_t(float, float, float)> colorFunc);
};

} // namespace render

} // namespace xy
